﻿using Framework.Core.Consts;
using Framework.Core.Options;
using Lazy.Captcha.Core;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using my.abp.Domain.Shared.Etos;
using System.ComponentModel;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Volo.Abp;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.EventBus.Local;
using Volo.Abp.EventBus.RabbitMq;
using Volo.Abp.Security.Claims;
using Yitter.IdGenerator;

namespace my.abp.Services
{
	[Authorize]
	public class AccountService : ApplicationService
	{
		private readonly JwtOptions _jwtOptions;
		private IHttpContextAccessor _httpContextAccessor;
		private readonly ILocalEventBus _localEventBus;
		private readonly IDistributedEventBus _distributedEventBus;

		public AccountService(IOptions<JwtOptions> jwtOptions, IHttpContextAccessor httpContextAccessor,
			ILocalEventBus localEventBus, RabbitMqDistributedEventBus distributedEventBus)
		{
			_jwtOptions = jwtOptions.Value;
			_httpContextAccessor = httpContextAccessor;
			_localEventBus = localEventBus;
			_distributedEventBus = distributedEventBus;
		}

		/// <summary>
		/// 登录
		/// </summary>
		/// <param name="input"></param>
		/// <returns></returns>
		[AllowAnonymous]
		public async Task<object> PostLoginAsync()
		{

			//获取token
			var accessToken = await GetTokenByUserIdAsync(1);

			return new { Token = accessToken };
		}

		/// <summary>
		/// 获取token
		/// </summary>
		/// <param name="userId"></param>
		/// <returns></returns>
		/// <exception cref="UserFriendlyException"></exception>
		private async Task<string> GetTokenByUserIdAsync(long Id)
		{
			// 这里抛出一个登录的事件
			if (_httpContextAccessor.HttpContext is not null)
			{
				LoginEventArgs loginEto = new LoginEventArgs();
				loginEto.UserName = "test";
				loginEto.UserId = Id;
				//await _localEventBus.PublishAsync(loginEto);
				await _distributedEventBus.PublishAsync(loginEto);
			}

			var accessToken = CreateToken(this.UserInfoToClaim(Id));
			//将用户信息添加到缓存中，需要考虑的是更改了用户、角色、菜单等整个体系都需要将缓存进行刷新，看具体业务进行选择

			return accessToken;
		}

		/// <summary>
		/// 创建令牌
		/// </summary>
		/// <param name="kvs"></param>
		/// <returns></returns>
		private string CreateToken(List<KeyValuePair<string, string>> kvs)
		{
			var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtOptions.SecurityKey));
			var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
			var claims = kvs.Select(x => new Claim(x.Key, x.Value.ToString())).ToList();
			var token = new JwtSecurityToken(
			   issuer: _jwtOptions.Issuer,
			   audience: _jwtOptions.Audience,
			   claims: claims,
			   expires: DateTime.Now.AddMinutes(_jwtOptions.ExpiresMinuteTime),
			   notBefore: DateTime.Now,
			   signingCredentials: creds);
			string returnToken = new JwtSecurityTokenHandler().WriteToken(token);

			return returnToken;
		}

		/// <summary>
		/// 令牌转换
		/// </summary>
		/// <param name="dto"></param>
		/// <returns></returns>
		private List<KeyValuePair<string, string>> UserInfoToClaim(long Id)
		{
			var claims = new List<KeyValuePair<string, string>>();
			AddToClaim(claims, AbpClaimTypes.UserId, Id.ToString());
			AddToClaim(claims, AbpClaimTypes.UserName, "test");

			return claims;
		}

		private void AddToClaim(List<KeyValuePair<string, string>> claims, string key, string value)
		{
			claims.Add(new KeyValuePair<string, string>(key, value));
		}
	}
}
